//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
/// Where did I derive this from ? Forgot m(__)m ...
///
//==================================================================

#include <math.h>
#include "Noise.h"

//==================================================================
static void normalizeArray( float *datap, int dims )
{
	float	sum_sq = 0;
	for (int i=0; i < dims; ++i)
		sum_sq += datap[i] * datap[i]; 

	float	oomag = 1.0f / sqrtf( sum_sq );
	for (int i=0; i < dims; ++i)
		datap[i] *= oomag; 
}

//==================================================================
static void swap( u_char &a, u_char &b )
{
	u_char	t = a;
	a = b;
	b = t;
}

//==================================================================
void Noise::Init(int nDimensions, u_int nSeed)
{
	PASSERT( nDimensions <= MAX_DIMENSIONS );
	_dims = nDimensions;

	Randomizer r(nSeed);

	int	i;
	for(i=0; i<256; i++)
	{
		_n_map[i] = i;

		for(int j=0; j<_dims; j++)
			_n_buffer[i][j] = r.NextFloat() - 0.5f;//(rand() & 1023) / 1023.0f - 0.5f;//r.NextFloat() - 0.5f;

		normalizeArray( _n_buffer[i], _dims );
	}

	while (--i)
	{
		int j = r.NextUInt() & 255;
		swap(_n_map[i], _n_map[j]);
	}
}

//==================================================================
static inline float lerp(float a, float b, float x)
{
	return a + x * (b - a);
}

//==================================================================
static inline float cubic( float a )
{
	return a * a * (3 - 2 * a);
}

//==================================================================
static inline int floor_int(float a)
{
	return (int)a - (a < 0 && a != (int)a);
}

//==================================================================
float Noise::CalcNoise(float *f)
{
	int i, n[MAX_DIMENSIONS];			// Indexes to pass to lattice function
	float r[MAX_DIMENSIONS];		// Remainders to pass to lattice function
	float w[MAX_DIMENSIONS];		// Cubic values to pass to interpolation function

	for(i=0; i<_dims; i++)
	{
		n[i] = (int)floorf( f[i] );
		r[i] = f[i] - n[i];
		w[i] = cubic( r[i] );
	}

	float fValue = 0;

	switch(_dims)
	{
		case 1:
			{
				fValue = lerp( lattice(n[0], r[0]), lattice(n[0]+1, r[0]-1), w[0] );

			}
			break;
		case 2:
			{
				float	la = lattice(n[0]  , r[0]  , n[1]  , r[1]  );
				float	lb = lattice(n[0]+1, r[0]-1, n[1]  , r[1]  );
				float	lc = lattice(n[0]  , r[0]  , n[1]+1, r[1]-1);
				float	ld = lattice(n[0]+1, r[0]-1, n[1]+1, r[1]-1);

				fValue = lerp( lerp(la,lb,w[0]), lerp(lc,ld,w[0]), w[1] );
			}
			break;
		case 3:
			fValue = lerp(lerp(lerp(lattice(n[0], r[0], n[1], r[1], n[2], r[2]),
									lattice(n[0]+1, r[0]-1, n[1], r[1], n[2], r[2]),
									w[0]),
							   lerp(lattice(n[0], r[0], n[1]+1, r[1]-1, n[2], r[2]),
									lattice(n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2], r[2]),
									w[0]),
							   w[1]),
						  lerp(lerp(lattice(n[0], r[0], n[1], r[1], n[2]+1, r[2]-1),
									lattice(n[0]+1, r[0]-1, n[1], r[1], n[2]+1, r[2]-1),
									w[0]),
							   lerp(lattice(n[0], r[0], n[1]+1, r[1]-1, n[2]+1, r[2]-1),
									lattice(n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2]+1, r[2]-1),
									w[0]),
							   w[1]),
						  w[2]);
			break;
		case 4:
			fValue = lerp(lerp(lerp(lerp(lattice(n[0], r[0], n[1], r[1], n[2], r[2], n[3], r[3]),
										 lattice(n[0]+1, r[0]-1, n[1], r[1], n[2], r[2], n[3], r[3]),
										 w[0]),
									lerp(lattice(n[0], r[0], n[1]+1, r[1]-1, n[2], r[2], n[3], r[3]),
										 lattice(n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2], r[2], n[3], r[3]),
										 w[0]),
									w[1]),
									lerp(lerp(lattice(n[0], r[0], n[1], r[1], n[2]+1, r[2]-1, n[3], r[3]),
										 lattice(n[0]+1, r[0]-1, n[1], r[1], n[2]+1, r[2]-1, n[3], r[3]),
										 w[0]),
									lerp(lattice(n[0], r[0], n[1]+1, r[1]-1, n[2]+1, r[2]-1),
										 lattice(n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2]+1, r[2]-1, n[3], r[3]),
										 w[0]),
									w[1]),
							   w[2]),
						  lerp(lerp(lerp(lattice(n[0], r[0], n[1], r[1], n[2], r[2], n[3]+1, r[3]-1),
										 lattice(n[0]+1, r[0]-1, n[1], r[1], n[2], r[2], n[3]+1, r[3]-1),
										 w[0]),
									lerp(lattice(n[0], r[0], n[1]+1, r[1]-1, n[2], r[2], n[3]+1, r[3]-1),
										 lattice(n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2], r[2], n[3]+1, r[3]-1),
										 w[0]),
									w[1]),
									lerp(lerp(lattice(n[0], r[0], n[1], r[1], n[2]+1, r[2]-1, n[3]+1, r[3]-1),
										 lattice(n[0]+1, r[0]-1, n[1], r[1], n[2]+1, r[2]-1, n[3]+1, r[3]-1),
										 w[0]),
									lerp(lattice(n[0], r[0], n[1]+1, r[1]-1, n[2]+1, r[2]-1),
										 lattice(n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2]+1, r[2]-1, n[3]+1, r[3]-1),
										 w[0]),
									w[1]),
							   w[2]),
						  w[3]);
			break;
	}

	//fValue *= 2;
	PASSERT( fValue >= -1 && fValue <= 1 );

	return fValue;
}
